package com.wdcg.activity;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.wdcg.R;
import com.wdcg.model.access.DwarfAccess;
import com.wdcg.model.dao.DwarfDescription;
import com.wdcg.utils.GameLogic;

public class PuzzleGameActivity extends Activity {
	
	public final static String PARAM_DWARF_ID = "DwarfID";
	public ImageView img;
	ImageView[][] puzzleImgs;
	int puzzleDimension;
	RelativeLayout rl;
	
	Point currPuzzleImagesLocations[][];
	Point[][] initialLocations;
	int screenPieceWidth;
	int screenPieceHeight;
	int freeField[];
	DwarfDescription dwarfDesc;
	boolean firstInit;
	Button startButton;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.puzzle_game);
		dwarfDesc=DwarfAccess.getDwarfDescriptionForDwarfId(getIntent().getIntExtra(PARAM_DWARF_ID, -1));
		firstInit=false;
		rl = (RelativeLayout) findViewById(R.id.puzzle_relative_layout);
		startButton=(Button)findViewById(R.id.startButton);
		puzzleDimension=3;

		//set header title
		setTitle(getString(R.string.puzzle_game_title));
		
		ViewTreeObserver observer = rl.getViewTreeObserver();
		observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
			
		    public void onGlobalLayout() {
		        //in here, place the code that requires you to know the dimensions.
		    	 if(!firstInit && displaySize()[0]>0){
					 firstInit=true;
					 initGame();
					 
					 startButton.setOnClickListener(new View.OnClickListener() {
							public void onClick(View v) {
								startButton.setAlpha(0);
								startButton.setEnabled(false);
								 for(int i=0; i<puzzleDimension; i++){
									 for(int j=0; j<puzzleDimension; j++){
										 if(i==puzzleDimension-1 && j==puzzleDimension-1){
											 break;
										 }
										 puzzleImgs[i][j].setAlpha(255);
									 }
								 }
								 animateShuffling(new int[]{-1,-1},1);
							}
						});
					 
				 }
		    }
		});
	}
	
void handleGameFinished(){
	for(int i=0; i<puzzleDimension; i++){
		for(int j=0; j<puzzleDimension; j++){
			if(i==puzzleDimension-1 && j==puzzleDimension-1){
				DwarfAccess.setDwarfGameScore(dwarfDesc.getDwarf(), true, false, false);
				
				final AlertDialog alertDialog = new AlertDialog.Builder(this).create();
				alertDialog.setTitle(getString(R.string.puzzle_game_success_alert_title));
				alertDialog.setMessage(getString(R.string.puzzle_game_success_alert_message));
				alertDialog.setButton(Dialog.BUTTON_NEUTRAL,getString(R.string.game_alert_close_button), new DialogInterface.OnClickListener(){
					public void onClick(DialogInterface dialog, int which) {
						GameLogic.setDwarfUnlocked(dwarfDesc.getDwarf());
						GameLogic.getMoreDwarfsVisible();
						alertDialog.dismiss();	
						finish();
					}
				});
				alertDialog.show();
				return;
			}
			ImageView view=puzzleImgs[i][j];
			Point initial= initialLocations[i][j];
			Point currPoint= currPuzzleImagesLocations[i][j];
			currPoint=initialLocations[currPoint.x][currPoint.y];
			if(currPoint.x!=initial.x || currPoint.y!=initial.y){
				return;
			}
		}
	}
}


	public void initGame(){
		if(dwarfDesc==null){
			return;
		}
		
		puzzleImgs=new ImageView[puzzleDimension][puzzleDimension];
		
		
		Log.i("loading game for dwarf", dwarfDesc.getName());
		Log.i("loading image",dwarfDesc.getDwarf().getImgTitle());
		Log.i("loading image with id",getResources().getIdentifier("@drawable/"+dwarfDesc.getDwarf().getImgTitle(), null, getPackageName())+" ");

		
		final Bitmap SOURCE_BITMAP = BitmapFactory.decodeResource(getResources(), getResources().getIdentifier("@drawable/"+dwarfDesc.getDwarf().getImgTitle(), null, getPackageName()));
		int bestBitmapPuzzleSize[]=getBestSize(SOURCE_BITMAP.getWidth(),SOURCE_BITMAP.getHeight());
		
		int[] screenSize= this.displaySize();
		int bestScreenPuzzleSize[]=getBestSize(screenSize[0],screenSize[1]);

		int bitmapPieceWidth=bestBitmapPuzzleSize[0]/puzzleDimension;
		int bitmapPieceHeight=bestBitmapPuzzleSize[1]/puzzleDimension;
		int currBitmapX=(int) ((SOURCE_BITMAP.getWidth()-bestBitmapPuzzleSize[0])*0.5);
		int startX=currBitmapX;
		int currBitmapY=(int) ((SOURCE_BITMAP.getHeight()-bestBitmapPuzzleSize[1])*0.5);

		screenPieceWidth= bestScreenPuzzleSize[0]/puzzleDimension;
		screenPieceHeight= bestScreenPuzzleSize[1]/puzzleDimension;
		int currScreenX=(int) ((screenSize[0]-bestScreenPuzzleSize[0])*0.5);
		int screenStartX=currScreenX;
		int currScreenY=(int) ((screenSize[1]-bestScreenPuzzleSize[1])*0.5);

		currPuzzleImagesLocations=new Point[puzzleDimension][puzzleDimension];
		freeField= new int[]{puzzleDimension-1,puzzleDimension-1};
		initialLocations=new Point[puzzleDimension][puzzleDimension];
		
		for(int i=0;i<puzzleDimension; i++){
			for(int j=0; j< puzzleDimension ; j++){
				
				initialLocations[i][j]=new Point(currScreenX,currScreenY);
				
				if(i==puzzleDimension-1 && j==puzzleDimension-1){
					return;
				}
				
				Bitmap newBitmap = Bitmap.createBitmap(SOURCE_BITMAP, currBitmapX,currBitmapY, bitmapPieceWidth, bitmapPieceHeight, null, false);
				ImageView image = new ImageView(this);
				image.setImageBitmap(newBitmap);
				image.setAlpha(128);
				image.setEnabled(false);
				puzzleImgs[i][j]=image;
				currPuzzleImagesLocations[i][j]=new Point(i,j);
	
				final int tmpImageDestIPos=i;
				final int tmpImageDestJPos=j;

				image.setOnTouchListener(new View.OnTouchListener() {
					final int imageInitialIPos=tmpImageDestIPos;
					final int imageInitialJPos=tmpImageDestJPos;
					
					Point imageInitialTouchPos;
					
					RelativeLayout.LayoutParams prevParams;
					
					int startX;
					int startY;
					
					int moveRangeRect[];
					VelocityTracker velocityTracker;
					
					public boolean onTouch(View v, MotionEvent event) {
						
						//compute area to move
						if(event.getAction()==MotionEvent.ACTION_DOWN){
							if(velocityTracker==null){
								velocityTracker=VelocityTracker.obtain();
								}else{
									velocityTracker.clear();
								}
							
							startX=(int) event.getX();
							startY=(int) event.getY();
							
							imageInitialTouchPos=currPuzzleImagesLocations[imageInitialIPos][imageInitialJPos];
							
							moveRangeRect= getRectMoveForImage(imageInitialIPos, imageInitialJPos);
						}
						
						RelativeLayout.LayoutParams params;
						params = new RelativeLayout.LayoutParams(v.getLayoutParams().width, v.getLayoutParams().height);

						params.leftMargin = (int) (v.getX()+event.getX()-startX);
						params.topMargin = (int) (v.getY()+event.getY()-startY);
						
						// touch move
						if(params.leftMargin<moveRangeRect[0]){
							params.leftMargin=moveRangeRect[0];
						}
						if(params.topMargin<moveRangeRect[1]){
							params.topMargin=moveRangeRect[1];
						}
						if(params.leftMargin+v.getLayoutParams().width>moveRangeRect[0]+moveRangeRect[2]){
							params.leftMargin=moveRangeRect[0]+moveRangeRect[2]-v.getLayoutParams().width;
						}
						if(params.topMargin+v.getLayoutParams().height>moveRangeRect[1]+moveRangeRect[3]){
							params.topMargin=moveRangeRect[1]+moveRangeRect[3]-v.getLayoutParams().height;
						}
						
						//move puzzle to proper area
						if(event.getAction()==MotionEvent.ACTION_UP){
							int startXAnim=params.leftMargin;
							int startYAnim=params.topMargin;
							float velocity;
							velocityTracker.computeCurrentVelocity(1);
							
							RelativeLayout.LayoutParams startAnimParams=new RelativeLayout.LayoutParams(params.width,params.height);
							startAnimParams.leftMargin=params.leftMargin;
							startAnimParams.topMargin=params.topMargin;
							if(moveRangeRect[2]>moveRangeRect[3]){
								velocity=velocityTracker.getXVelocity();
								
								if(params.leftMargin>moveRangeRect[0]+screenPieceWidth*0.5 ){
									params.leftMargin=moveRangeRect[0]+screenPieceWidth;
								}else{
									params.leftMargin=moveRangeRect[0];
								}
							}else{								
								velocity=velocityTracker.getYVelocity();
								velocity=Math.abs(velocity);

								if(params.topMargin>moveRangeRect[1]+screenPieceHeight*0.5 ){
									params.topMargin=moveRangeRect[1]+screenPieceHeight;
								}else{
									params.topMargin=moveRangeRect[1];
								}
							}
							
							Log.i("velocity", velocity+"");
							int[] newPuzzlePos = setPuzzlePositionOnScreen(params.leftMargin, params.topMargin,imageInitialIPos, imageInitialJPos);
							
							if(newPuzzlePos[0]==freeField[0] && newPuzzlePos[1]==freeField[1]){
								freeField[0]=imageInitialTouchPos.x;
								freeField[1]=imageInitialTouchPos.y;
							}
							
							TranslateAnimation animation = new TranslateAnimation(0, params.leftMargin-startXAnim, 0, params.topMargin-startYAnim);
							animation.setDuration(getAnimationDuration(velocity, startAnimParams, params));
							animation.setFillAfter(false);
							MyAnimationListener listener = new MyAnimationListener();
							listener.animated=(ImageView) v;
							listener.destination=params;
							animation.setAnimationListener(listener);
							v.startAnimation(animation);
					        

							return false;
						}
						velocityTracker.addMovement(event);
						
						prevParams=new RelativeLayout.LayoutParams(params.width, params.height);
						prevParams.leftMargin=params.leftMargin;
						prevParams.topMargin=params.topMargin;
						v.setLayoutParams(params);
						return true;
					}
					
				});
				
				puzzleImgs[i][j]=image;
				RelativeLayout.LayoutParams params;
				params = new RelativeLayout.LayoutParams(screenPieceWidth, screenPieceHeight);
				params.leftMargin = currScreenX;
				params.topMargin = currScreenY;
				

				currScreenX+=screenPieceWidth;
				currBitmapX+=bitmapPieceWidth;

				rl.addView(image,params);
			}
			currScreenX=screenStartX;
			currBitmapX=startX;
			
			currBitmapY+=bitmapPieceWidth;
			currScreenY+=screenPieceHeight;
		}
	}
	public int[] getBestSize(int w, int h){
		int min = w<h?w:h;
		return new int[]{min,min};
	}
	
	public int[] displaySize(){

		int width = rl.getWidth();
		int height = rl.getHeight();
		return new int[] {width, height};
	}
	
	public int[] getRectMoveForImage(int i, int j){
		int currPuzzleI=currPuzzleImagesLocations[i][j].x;
		int currPuzzleJ=currPuzzleImagesLocations[i][j].y;
		
		Point freeScreenLocation;
		Point currScreenLocation=initialLocations[currPuzzleI][currPuzzleJ];
		
		if(isFree(currPuzzleI,currPuzzleJ-1)){
			freeScreenLocation=initialLocations[currPuzzleI][currPuzzleJ-1];
			return new int[]{freeScreenLocation.x, freeScreenLocation.y, 2*this.screenPieceWidth, this.screenPieceHeight};
		} else if(isFree(currPuzzleI,currPuzzleJ+1)){
			freeScreenLocation=initialLocations[currPuzzleI][currPuzzleJ+1];
			return new int[]{currScreenLocation.x, currScreenLocation.y, 2*this.screenPieceWidth, this.screenPieceHeight};
		} else if(isFree(currPuzzleI-1,currPuzzleJ)){
			freeScreenLocation=initialLocations[currPuzzleI-1][currPuzzleJ];
			return new int[]{freeScreenLocation.x, freeScreenLocation.y, this.screenPieceWidth, 2*this.screenPieceHeight};
		} else if(isFree(currPuzzleI+1,currPuzzleJ)){
			freeScreenLocation=initialLocations[currPuzzleI+1][currPuzzleJ];
			return new int[]{currScreenLocation.x, currScreenLocation.y, this.screenPieceWidth, 2*this.screenPieceHeight};
		}
		return new int[]{currScreenLocation.x,currScreenLocation.y, screenPieceWidth, screenPieceHeight};
		
	}
	
	public boolean isFree(int i, int j){
		if(i<0 || j< 0 || i>=this.puzzleDimension || j>=this.puzzleDimension){
			return false;
		}
		return freeField[0]==i&&freeField[1]==j;	
	}
	
	public int[] setPuzzlePositionOnScreen(int x, int y, int i, int j){
		Point location;
		
		for(int k=0; k< initialLocations.length; k++){
			for(int l=0; l< initialLocations[k].length; l++){
				location=initialLocations[k][l];
				if(location.x==x&&location.y==y){
					currPuzzleImagesLocations[i][j]=new Point(k,l);
					return new int[]{k,l};
				}
			}
		}
		return new int[]{-1,-1};
	}
	
	private class MyAnimationListener implements AnimationListener{
		public ImageView animated;
		LayoutParams destination;
		
	    public void onAnimationEnd(Animation animation) {
	    	Log.i("animation finished", "finished");
	        animated.clearAnimation();
	        animated.setLayoutParams(destination);
	        handleGameFinished();
	    }

	    public void onAnimationRepeat(Animation animation) {
	    }

	    public void onAnimationStart(Animation animation) {
	    }
	}
	
	private long getAnimationDuration(double v, RelativeLayout.LayoutParams currParams,RelativeLayout.LayoutParams destParams){
		double destDistance=distance(destParams,currParams);
		v=Math.abs(v);
		if(v<0.4){
			v= 0.4;
		}
		return (long) (destDistance/v);
	}
	
	public double distance(RelativeLayout.LayoutParams prevParams,RelativeLayout.LayoutParams currParams){
		double dxp=Math.pow(currParams.leftMargin-prevParams.leftMargin,2);
		double dyp=Math.pow(currParams.topMargin-prevParams.topMargin,2) ;
		return Math.sqrt(dxp+dyp);
	}
	
	final int animations=30;
	public void animateShuffling(int[] locationToOmnit, final int animationIndex){
		final int[] prevFree=freeField;
		final int[] puzzleIndexToMove=getRandomMoveableIndex(locationToOmnit);
		final int[] puzzleInitialLocation=getImageForCurrLocation(new Point(puzzleIndexToMove[0],puzzleIndexToMove[1]));
		final ImageView v=puzzleImgs[puzzleInitialLocation[0]][puzzleInitialLocation[1]];
		
		Point destLocation=initialLocations[freeField[0]][freeField[1]];
		Point startLocation=initialLocations[puzzleIndexToMove[0]][puzzleIndexToMove[1]];
		
		final RelativeLayout.LayoutParams params;
		params = new RelativeLayout.LayoutParams(v.getLayoutParams().width, v.getLayoutParams().height);
		params.leftMargin =  (destLocation.x);
		params.topMargin =  (destLocation.y);
		
		TranslateAnimation animation = new TranslateAnimation(0, destLocation.x-startLocation.x, 0, destLocation.y-startLocation.y);
		double timming=Math.sin(((double)animationIndex/(double)animations)*Math.PI);
		timming*=timming;
		animation.setDuration((long)(180.0-(140.0*timming)));
		animation.setFillAfter(false);

        
		AnimationListener listener = new AnimationListener(){

			public void onAnimationEnd(Animation animation) {
				v.clearAnimation();
		        v.setLayoutParams(params);
		        freeField=puzzleIndexToMove;
		        setPuzzlePositionOnScreen(params.leftMargin, params.topMargin,puzzleInitialLocation[0], puzzleInitialLocation[1]);
		        if(animationIndex<animations){
		        animateShuffling(prevFree, animationIndex+1);
		        }else{
		        	 for(int i=0; i<puzzleDimension; i++){
						 for(int j=0; j<puzzleDimension; j++){
							 if(i==puzzleDimension-1 && j==puzzleDimension-1){
								 break;
							 }
							 //puzzleImgs[i][j].setAlpha(255);
							 puzzleImgs[i][j].setEnabled(true);
						 }
					 }
		        }
			}

			public void onAnimationRepeat(Animation animation) {

			}

			public void onAnimationStart(Animation animation) {

			}
			
		};
		
		animation.setAnimationListener(listener);
		v.startAnimation(animation);
	}
	
	public int[] getRandomMoveableIndex(int[] excluding){
		int[] result = new int[]{freeField[0],freeField[1]};
		
		int selection = (int) (Math.random()*4);
		if(selection==0){
			result[0]+=1;
		}else if(selection==1){
			result[0]-=1;
		}else if(selection==2){
			result[1]+=1;
		}else if(selection==3){
			result[1]-=1;
		}
		
		if(result[0]<0 || result[1]<0||result[0]>=puzzleDimension||result[1]>=puzzleDimension || (excluding[0]==result[0]&&excluding[1]==result[1])){
			return getRandomMoveableIndex(excluding);
		}else{
			return result;
		}
	}
	
	public int[] getImageForCurrLocation(Point vLocation){
		Point location;
		for(int k=0; k<currPuzzleImagesLocations.length; k++){
			for(int l=0; l< currPuzzleImagesLocations[k].length; l++){
				location=currPuzzleImagesLocations[k][l];
				if(location.x==vLocation.x&&location.y==vLocation.y){
					return  new int[]{k,l};
				}
			}
		}
		return null;
	}
}
